package cn.donting.jme.ioc;

import cn.donting.jme.utile.ResourcesUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ClassUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 一个简易 的 ioc 容器
 * 没有依赖注入
 */
public class IocContainer {

    private final List<Object> objects = new ArrayList<>();
    private final Set<Class> classMetas = new HashSet<>();
    private final HashMap<Class, Object> objectsMap = new HashMap<>();
    private static final HashMap<Class, List<Object>> caching = new HashMap<>();
    private static Set<String> allResources;

    public IocContainer() {
        try {
            init();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void init() throws Exception {
        Set<Class<?>> classes = ClassUtil.scanPackage("cn");
        for (Class<?> aClass : classes) {
            if (aClass.getAnnotation(Bean.class) != null) {
                Object o = aClass.newInstance();
                objects.add(o);
                objectsMap.put(o.getClass(), o);
            }
            if (getParentAnnotation(aClass, ClassMeta.class)) {
                classMetas.add(aClass);
            }
        }

        Set<String> allResources = new ResourcesUtil().scanResources("cn", "com","org");
        IocContainer.allResources = allResources.stream().filter(s -> !s.endsWith(".class")).collect(Collectors.toSet());
    }

    /**
     * Class 及其父类是否有 注解
     *
     * @param aclass
     * @param
     * @return
     */
    private boolean getParentAnnotation(Class aclass, Class<? extends Annotation> annotationClass) {
        if (aclass == null) {
            return false;
        }
        Annotation annotation = aclass.getAnnotation(annotationClass);
        if (annotation != null) {
            return true;
        }
        return getParentAnnotation(aclass.getSuperclass(), annotationClass);
    }

    public <T> List<T> getBeansByTypes(Class<T> tClass) {
        List builders = caching.get(tClass);
        if (builders == null) {
            builders = new ArrayList<>();
            for (Object object : objects) {
                if (tClass.isAssignableFrom(object.getClass())) {
                    builders.add(object);
                }
            }
            builders.sort((o1, o2) -> {
                Sort sort1 = o1.getClass().getAnnotation(Sort.class);
                int sorto1 = Integer.MAX_VALUE;
                if (sort1 != null) {
                    sorto1 = sort1.value();
                }
                Sort sort2 = o2.getClass().getAnnotation(Sort.class);
                int sorto2 = Integer.MAX_VALUE;
                if (sort2 != null) {
                    sorto2 = sort2.value();
                }
                if (sorto2 == sorto1) {
                    return o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName());
                }
                return Integer.compare(sorto1, sorto2);
            });
            caching.put(tClass, builders);

        }
        return (List<T>) builders;
    }

    public <T> T getBeansByType(Class<T> tClass) {
        return (T) objectsMap.get(tClass);
    }

    /**
     * 获取某个类型 的 tClass及其子类
     * 不包括抽象类和接口
     *
     * @param tClass
     */
    public <T> List<Class<T>> getClassMetas(Class<T> tClass) {
        List<Class<T>> list = new ArrayList<>();
        for (Class classMeta : classMetas) {
            if (tClass.isAssignableFrom(classMeta)
                    && !Modifier.isAbstract(classMeta.getModifiers())
                    && !Modifier.isInterface(classMeta.getModifiers())
            ) {
                list.add(classMeta);
            }
        }

        return list;
    }

    /**
     * 获取被注解标记的类
     *
     * @param tClass
     * @return
     */
    public List getBeansByAnnotation(Class<? extends Annotation> tClass) {
        List objects1 = caching.get(tClass);
        if (objects1 == null) {
            objects1 = new ArrayList<>();
            for (Object object : objects) {
                if (object.getClass().getAnnotation(tClass) != null) {
                    objects1.add(object);
                }
            }
            caching.put(tClass, objects1);
        }
        return objects1;
    }


    public List<String> getResourcesName(String extName){
        return allResources.stream().filter(s -> s.endsWith(extName)).collect(Collectors.toList());
    }

}
